/* map.c is part of VIP program
 * Written by J. Salvador Arias, 2010
 *      CONICET, INSUE, Instituto Miguel Lillo,
 *      Miguel Lillo 205, S.M. de Tucuman (4000)
 *      Tucuman, Argentina
 *      e-mail: jsalarias@csnat.unt.edu.ar
 *
 * VIP is a free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public
 * License as published by the Free Software Foundation and
 * Creative Commons.
 *
 * This file and the VIP program is distributed in the hope
 * that it will be useful, but WITHOUT ANY WARRANTY; without
 * even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE.
 * See the GNU General Public License for more details.
 *
 * To view a copy of this license, visit
 * http://creativecommons.org/licenses/GPL/2.0/
 * or send a letter to
 * Creative Commons, 171 Second Street, Suite 300,
 * San Francisco, California, 94105, USA.
 */

#include "wins.h"
#include "dialogs.h"
#include "parser.h"
#include "grid.h"
#include "tessellation.h"
#include "out.h"
#include "mapdraw.h"

typedef struct tagMAPWINDOWDATA MAPWINDATA;
struct tagMAPWINDOWDATA {
 GdkPixbuf* mapImg;
 GdkPixmap* pixMap;
 GtkWidget* drawable;

 GdkGC* blackPen;
 GdkGC* greenPen;
 GdkGC* bluePen;
 GdkGC* redPen;
 GdkGC* greyPen;
 GdkGC* whitePen;

 GtkWidget* menuDrawGrid;
 GtkWidget* menuDrawBarrier;
 GtkWidget* menuDrawRecords;
 GtkWidget* menuDrawCells;
 GtkWidget* menuDrawAll;
 GtkWidget* menuDrawRemoved;
 GtkWidget* menuScaledRecords;
 GtkWidget* menuBarrier;
 GtkWidget* menuBarrierVoronoi;
 GtkWidget* menuBarrierDelaunay;
 GtkWidget* menuBarrierMidTri;
 GtkWidget* menuBarrierAncestor;
 GtkWidget* menuBarrierCommon;

 GtkWidget* labelMousePos;
 GtkWidget* labelActNode;

 gint clientX;
 gint clientY;
 SCOUNTER width;
 SCOUNTER height;
 SCOUNTER maxY;
 SINTEGER originX;
 SINTEGER originY;
 SINTEGER startX;
 SINTEGER startY;
 SREAL north;
 SREAL south;
 SREAL west;
 SREAL east;
 gint prevX;
 gint prevY;
 gint radius;

 SREAL xScale;
 SREAL yScale;

 GNode* actNode;

 SFLAG mode;
 SFLAG barrierMode;
 SFLAG viewMode;
};

#define DRAW_MAP_MODE 0
#define EDIT_MAP_MODE 1

#define VIEW_RECORDS 0
#define VIEW_CELLS   1

#define DRAW_AS_VORONOI  0
#define DRAW_AS_DELAUNAY 1
#define DRAW_AS_MIDTRI   2

#define MENU_VORONOI  "voronoi_barrier"
#define MENU_DELAUNAY "delaunay_barrier"
#define MENU_MIDTRI "midtri_barrier"


static gint OnDelete (GtkWidget* mapWin, GdkEvent* event, gpointer data);
static gint OnExpose (GtkWidget* drawable, GdkEventExpose* event, gpointer data);
static gint OnConfigure (GtkWidget* drawable, GdkEvent* event, gpointer data);

static gint OnMotion (GtkWidget* drawable, GdkEventMotion* event, gpointer data);
static gint OnMouseButton (GtkWidget* drawable, GdkEventButton* event, gpointer data);

static void OnMenuLoadRaster (GtkMenuItem* loadMenu, gpointer data);
static void OnMenuLoadVector (GtkMenuItem* loadMenu, gpointer data);
static void OnMenuSave (GtkMenuItem* saveMenu, gpointer data);
static void OnMenuLimits (GtkMenuItem* limitsMenu, gpointer data);
static void OnMenuCentre (GtkMenuItem* centreMenu, gpointer data);
static void OnMenuRadius (GtkMenuItem* radiusMenu, gpointer data);
static void OnMenuViewMode (GtkMenuItem* drawCheckMenu, gpointer data);
static void OnMenuDrawCheck (GtkMenuItem* drawCheckMenu, gpointer data);
static void OnMenuScaledRecords (GtkMenuItem* scaledMenu, gpointer data);
static void OnMenuBarrierMode (GtkMenuItem* modeMenu, gpointer data);
static void OnMenuBarrierOptions (GtkMenuItem* barrierMenu, gpointer data);

static void PrepareGeoRefs (SFLAG updateAll);
static void PrepareVectorMap (SFLAG updateAll, SCOUNTER originX, SCOUNTER originY);

static void DrawMap ();
static void DrawPixMap (GdkPixmap* pixmap, SCOUNTER clientX, SCOUNTER clientY, SCOUNTER originX, SCOUNTER originY);
static void DrawActRec (GdkPixmap* pixmap, SCOUNTER clientX, SCOUNTER clientY, SCOUNTER originX, SCOUNTER originY,
                        GNode* refNode, GdkGC* pen, BITFIELD* outCells);
static GEOREF* SearchNearestPoint (gint x, gint y);

static void DrawBarrier (GdkPixmap* pixmap, SCOUNTER clientX, SCOUNTER clientY, SCOUNTER originX, SCOUNTER originY,
                         GList* triangles);
static void DrawBarrierFromRec (GdkPixmap* pixmap, SCOUNTER clientX, SCOUNTER clientY, SCOUNTER originX, SCOUNTER originY,
                                RECONS* rec);
static void DrawBarrierFromAlloSet (GdkPixmap* pixmap, SCOUNTER clientX, SCOUNTER clientY, SCOUNTER originX, SCOUNTER originY,
                                    PHYLONODE* node);

static void CheckYLimitsLimits (SCOUNTER* x1, SCOUNTER* y1, SCOUNTER* x2, SCOUNTER* y2);

static MAPWINDATA mWinData;

void StartMapWindow () {
 GtkWidget* gBox;
 GtkWidget* statusBarBox;
 GtkWidget* menuBar;
 GtkWidget* menu;
 GtkAccelGroup* accelGroup;
 GSList* group;

 GdkColor blueColor = {0, 0x0000, 0x0000, 0xFFFF};
 GdkColor greenColor = {0, 0x0000, 0xFFFF, 0x0000};
 GdkColor redColor = {0, 0xFFFF, 0x0000, 0x0000};
 GdkColor greyColor = {0, 0x6000, 0x6000, 0x6000};

 mWinData . pixMap = NULL;
 mWinData . mapImg = NULL;
 mWinData . width = 720;
 mWinData . height = 360;
 mWinData . maxY = 360;
 mWinData . originX = mWinData . originY = 0;
 mWinData . startX = mWinData . startY = 0;
 mWinData . north = mWinData . west = 0;
 mWinData . south = 180.0000;
 mWinData . east = 360.0000;
 mWinData . xScale = (SREAL) mWinData . width / (mWinData . east - mWinData . west);
 mWinData . yScale = (SREAL) mWinData . height / (mWinData . south - mWinData . north);
 mWinData . radius = 1;

 mWinData . mode = DRAW_MAP_MODE;

 SETMAPLINES (NULL);

 gBox = gtk_vbox_new (FALSE, 0);
 mapWindow = CreateWindow ("VIP: MapWindow", 100, 200, 800, 500, G_CALLBACK (OnDelete), gBox);

 /* --- Set menues --- */
 accelGroup = gtk_accel_group_new ();
 gtk_window_add_accel_group (GTK_WINDOW (mapWindow), accelGroup);
 menuBar = gtk_menu_bar_new ();
 gtk_box_pack_start (GTK_BOX (gBox), menuBar, FALSE, TRUE, 0);
 gtk_widget_show (menuBar);
 /* Map */
 menu = gtk_menu_new ();
 CreateMenuItem (menu, "_Open raster...", accelGroup, GDK_O, GDK_CONTROL_MASK, G_CALLBACK (OnMenuLoadRaster), NULL);
 CreateMenuItem (menu, "Open vecto_r...", accelGroup, GDK_T, GDK_CONTROL_MASK, G_CALLBACK (OnMenuLoadVector), NULL);
 CreateMenuSeparator (menu);
 CreateMenuItem (menu, "Map _limits...", accelGroup, GDK_L, GDK_CONTROL_MASK, G_CALLBACK (OnMenuLimits), NULL);
 CreateMenuItem (menu, "_Centre on...", accelGroup, GDK_C, GDK_CONTROL_MASK, G_CALLBACK (OnMenuCentre), NULL);
 CreateMenuSeparator (menu);
 CreateMenuItem (menu, "_Save map...", accelGroup, GDK_S, GDK_CONTROL_MASK | GDK_SHIFT_MASK, G_CALLBACK (OnMenuSave), NULL);
 AddMenuToBar (menu, "_Map", menuBar);
 /* Drawing */
 menu = gtk_menu_new ();
 group = NULL;
 mWinData . menuDrawRecords = CreateMenuItemRadio (menu, "View _records", accelGroup, GDK_R, GDK_CONTROL_MASK,
                                             G_CALLBACK (OnMenuViewMode), NULL, group);
 group = gtk_radio_menu_item_get_group (GTK_RADIO_MENU_ITEM (mWinData . menuDrawRecords));
 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (mWinData . menuDrawRecords), TRUE);
 mWinData . menuDrawCells = CreateMenuItemRadio (menu, "View c_ells", accelGroup, GDK_E, GDK_CONTROL_MASK,
                                             G_CALLBACK (OnMenuViewMode), NULL, group);
 gtk_widget_set_sensitive (mWinData . menuDrawCells, FALSE);
 CreateMenuSeparator (menu);
 CreateMenuItem (menu, "Record radi_us...", accelGroup, GDK_U, GDK_CONTROL_MASK, G_CALLBACK (OnMenuRadius), NULL);
 mWinData . menuScaledRecords = CreateMenuItemCheck (menu, "Phylogenticly _saled records", accelGroup, GDK_K, GDK_CONTROL_MASK,
                                                    G_CALLBACK (OnMenuScaledRecords), NULL);
 CreateMenuSeparator (menu);
 mWinData . menuDrawAll = CreateMenuItemCheck (menu, "_All records", accelGroup, GDK_A, GDK_CONTROL_MASK,
                                                    G_CALLBACK (OnMenuDrawCheck), NULL);
 mWinData . menuDrawRemoved = CreateMenuItemCheck (menu, "Re_moved records", accelGroup, GDK_M, GDK_CONTROL_MASK,
                                                    G_CALLBACK (OnMenuDrawCheck), NULL);
 mWinData . menuDrawBarrier = CreateMenuItemCheck (menu, "Barrier", accelGroup, GDK_B, GDK_CONTROL_MASK,
                                                    G_CALLBACK (OnMenuDrawCheck), NULL);
 CreateMenuSeparator (menu);
 mWinData . menuDrawGrid = CreateMenuItemCheck (menu, "_Grid", accelGroup, GDK_G, GDK_CONTROL_MASK,
                                                    G_CALLBACK (OnMenuDrawCheck), NULL);
 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (mWinData . menuDrawGrid), FALSE);
 gtk_widget_set_sensitive (mWinData . menuDrawBarrier, FALSE);
 gtk_widget_set_sensitive (mWinData . menuDrawRemoved, FALSE);
 AddMenuToBar (menu, "_Drawing", menuBar);
 /* Barrier */
 menu = gtk_menu_new ();
 group = NULL;
 mWinData . menuBarrierVoronoi = CreateMenuItemRadio (menu, "_Voronoi lines", accelGroup, GDK_V, GDK_CONTROL_MASK,
                                             G_CALLBACK (OnMenuBarrierMode), MENU_VORONOI, group);
 group = gtk_radio_menu_item_get_group (GTK_RADIO_MENU_ITEM (mWinData . menuBarrierVoronoi));
 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (mWinData . menuBarrierVoronoi), TRUE);
 mWinData . menuBarrierDelaunay = CreateMenuItemRadio (menu, "_Delaunay triangles", accelGroup, GDK_D, GDK_CONTROL_MASK,
                                             G_CALLBACK (OnMenuBarrierMode), MENU_DELAUNAY, group);
 group = gtk_radio_menu_item_get_group (GTK_RADIO_MENU_ITEM (mWinData . menuBarrierDelaunay));
 mWinData . menuBarrierMidTri = CreateMenuItemRadio (menu, "Mid tr_iangles", accelGroup, GDK_I, GDK_CONTROL_MASK,
                                             G_CALLBACK (OnMenuBarrierMode), MENU_MIDTRI, group);
 CreateMenuSeparator (menu);
 mWinData . menuBarrierAncestor = CreateMenuItemCheck (menu, "Barriers on a_ncestors", accelGroup, GDK_N, GDK_CONTROL_MASK,
                                                    G_CALLBACK (OnMenuBarrierOptions), NULL);
 mWinData . menuBarrierCommon = CreateMenuItemCheck (menu, "Common barr_iers (on consensus)", accelGroup, GDK_I, GDK_CONTROL_MASK,
                                                    G_CALLBACK (OnMenuBarrierOptions), NULL);
 mWinData . menuBarrier = AddMenuToBar (menu, "_Barriers", menuBar);
 gtk_widget_set_sensitive (mWinData . menuBarrier, FALSE);

 /* --- Drawing area --- */
 mWinData . drawable = gtk_drawing_area_new ();
 gtk_box_pack_start (GTK_BOX (gBox), mWinData . drawable, TRUE, TRUE, 0);
 gtk_widget_show (mWinData . drawable);
 g_signal_connect (G_OBJECT (mWinData . drawable), "expose_event", G_CALLBACK (OnExpose), NULL);
 g_signal_connect (G_OBJECT (mWinData . drawable), "configure_event", G_CALLBACK (OnConfigure), NULL);
 gtk_widget_add_events (mWinData . drawable, GDK_POINTER_MOTION_MASK | GDK_BUTTON_PRESS_MASK);
 g_signal_connect (G_OBJECT (mWinData . drawable), "motion_notify_event", G_CALLBACK (OnMotion), NULL);
 g_signal_connect (G_OBJECT (mWinData . drawable), "button_press_event", G_CALLBACK (OnMouseButton), NULL);

 /* --- status bar --- */
 statusBarBox = gtk_hbox_new (FALSE, 0);
 mWinData . labelActNode = gtk_label_new ("no node selected");
 gtk_label_set_justify (GTK_LABEL (mWinData . labelActNode), GTK_JUSTIFY_LEFT);
 gtk_box_pack_start (GTK_BOX (statusBarBox), mWinData . labelActNode, TRUE, TRUE, 0);
 gtk_widget_show (mWinData . labelActNode);
 mWinData . labelMousePos = gtk_label_new ("mouse off map");
 gtk_label_set_justify (GTK_LABEL (mWinData . labelMousePos), GTK_JUSTIFY_RIGHT);
 gtk_box_pack_start (GTK_BOX (statusBarBox), mWinData . labelMousePos, TRUE, TRUE, 0);
 gtk_widget_show (mWinData . labelMousePos);
 gtk_box_pack_start (GTK_BOX (gBox), statusBarBox, FALSE, TRUE, 0);
 gtk_widget_show (statusBarBox);

 gtk_widget_show (mapWindow);

 mWinData . blackPen = gdk_gc_new (mWinData . drawable -> window);
 gdk_gc_copy (mWinData . blackPen, mWinData . drawable -> style -> black_gc);

 mWinData . greenPen = gdk_gc_new (mWinData . drawable -> window);
 gdk_gc_copy (mWinData . greenPen, mWinData . drawable -> style -> black_gc);
 gdk_gc_set_rgb_fg_color (mWinData . greenPen, &greenColor);

 mWinData . bluePen = gdk_gc_new (mWinData . drawable -> window);
 gdk_gc_copy (mWinData . bluePen, mWinData . drawable -> style -> black_gc);
 gdk_gc_set_rgb_fg_color (mWinData . bluePen, &blueColor);

 mWinData . redPen = gdk_gc_new (mWinData . drawable -> window);
 gdk_gc_copy (mWinData . redPen, mWinData . drawable -> style -> black_gc);
 gdk_gc_set_rgb_fg_color (mWinData . redPen, &redColor);

 mWinData . greyPen = gdk_gc_new (mWinData . drawable -> window);
 gdk_gc_copy (mWinData . greyPen, mWinData . drawable -> style -> black_gc);
 gdk_gc_set_rgb_fg_color (mWinData . greyPen, &greyColor);

 mWinData . whitePen = gdk_gc_new (mWinData . drawable -> window);
 gdk_gc_copy (mWinData . whitePen, mWinData . drawable -> style -> white_gc);

 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (mWinData . menuDrawAll), TRUE);
 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (mWinData . menuScaledRecords), FALSE);
 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (mWinData . menuDrawRemoved), FALSE);
 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (mWinData . menuDrawRecords), TRUE);

 mWinData . actNode = NULL;
 mWinData . barrierMode = DRAW_AS_VORONOI;
 mWinData . viewMode = VIEW_RECORDS;
}

void ChangeGeo () {
 PHYLONODE* node;
 GString* text;

 PrepareGeoRefs (FALSE);
 if (activeNode != mWinData . actNode) {
   mWinData . actNode = activeNode;
 }
 if (activeNode == NULL)
   gtk_label_set_text (GTK_LABEL (mWinData . labelActNode), "no node selected");
 else {
   node = (PHYLONODE*) activeNode -> data;
   text = g_string_new ("Node: ");
   if (node -> name != NULL)
     g_string_append_printf (text, "%s, ", node -> name);
   else
     g_string_append_printf (text, "%ld, ", node -> id);
   if (node -> tree -> name != NULL)
     g_string_append_printf (text, "Tree: %s", node -> tree -> name);
   else
     g_string_append_printf (text, "Tree: %ld", node -> tree -> id);
   gtk_label_set_text (GTK_LABEL (mWinData . labelActNode), text -> str);
   g_string_free (text, TRUE);
 }
 DrawMap ();
}

void ActivateGrid (SFLAG mode) {
 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (mWinData . menuDrawGrid), mode);
 gtk_widget_set_sensitive (mWinData . menuDrawBarrier, mode);
 gtk_widget_set_sensitive (mWinData . menuDrawRemoved, mode);
 gtk_widget_set_sensitive (mWinData . menuDrawCells, mode);
 if (mode == FALSE) {
   mWinData . viewMode = VIEW_RECORDS;
   gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (mWinData . menuDrawRecords), TRUE);
 }
 DrawMap ();
}

void SetEdition (SFLAG mode) {
 if (mode == TRUE)
   mWinData . mode = EDIT_MAP_MODE;
 else
   mWinData . mode = DRAW_MAP_MODE;
}

void PaintNewGeoRef (GEOREF* geo) {
 geo -> actX = mWinData . xScale * geo -> azimuth;
 geo -> actY = mWinData . yScale * geo -> inclination;
 DrawMap ();
}

gint OnDelete (GtkWidget* treeWin, GdkEvent* event, gpointer data) {
 gtk_window_iconify (GTK_WINDOW (mapWindow));
 return TRUE;
}

gint OnExpose (GtkWidget* drawable, GdkEventExpose* event, gpointer data) {
 gdk_draw_pixmap (mWinData . drawable -> window, mWinData . drawable -> style -> fg_gc [GTK_WIDGET_STATE (mWinData . drawable)],
     mWinData . pixMap, event -> area . x, event -> area . y, event -> area . x, event -> area . y,
     event -> area . width, event -> area . height);
 return FALSE;
}

gint OnConfigure (GtkWidget* drawable, GdkEvent* event, gpointer data) {
 mWinData . clientX = mWinData . drawable -> allocation . width;
 mWinData . clientY = mWinData . drawable -> allocation . height;
 if (mWinData . pixMap)
   g_object_unref (mWinData . pixMap);
 mWinData . pixMap = gdk_pixmap_new (mWinData . drawable -> window, mWinData . clientX, mWinData . clientY, -1);
 DrawMap ();
 return TRUE;
}

gint OnMotion (GtkWidget* drawable, GdkEventMotion* event, gpointer data) {
 SINTEGER nu;
 SREAL nuLon, nuLat;
 gchar* text;

 if (event -> state & GDK_BUTTON1_MASK) {
   if ((mWinData . prevX != 0) || (mWinData . prevY != 0)) {
     nu = event -> x - mWinData . prevX;
     mWinData . originX += nu;
     if (mWinData . originX < - (mWinData . width - 10))
       mWinData . originX = - (mWinData . width - 10);
     else if (mWinData . originX > (mWinData . clientX - 10))
       mWinData . originX = mWinData . clientX - 10;
     nu = event -> y - mWinData . prevY;
     mWinData . originY += nu;
     if (mWinData . originY < - (mWinData . height - 10))
       mWinData . originY = - (mWinData . height - 10);
     else if (mWinData . originY > (mWinData . clientY - 10))
       mWinData . originY = mWinData . clientY - 10;
     PrepareVectorMap (FALSE, mWinData . originX, mWinData . originY);
     DrawMap ();
   }
   mWinData . prevX = event -> x;
   mWinData . prevY = event -> y;
 }
 else {
   mWinData . prevX = 0;
   mWinData . prevY = 0;
   nuLon = (SREAL) (event -> x - mWinData . originX) / mWinData . xScale;
   nuLat = (SREAL) (event -> y - mWinData . originY) / mWinData . yScale;
   if ((nuLon < 0) || (nuLon > 360) || (nuLat < 0) || (nuLat > 180))
     gtk_label_set_text (GTK_LABEL (mWinData . labelMousePos), "mouse off map");
   else {
     nuLat = FROMINCTOLAT (nuLat);
     nuLon = FROMAZMTOLON (nuLon);
     text = g_strdup_printf ("lat: %.4f\tlon: %.4f", nuLat, nuLon);
     gtk_label_set_text (GTK_LABEL (mWinData . labelMousePos), text);
     g_free (text);
   }
 }
 return TRUE;
}

gint OnMouseButton (GtkWidget* drawable, GdkEventButton* event, gpointer data) {
 GList* navGeo;
 GEOREF* actGeo;
 TERMINAL* term;
 PHYLONODE* actNode;
 GEOREFBROWSERDLG* browser;
 ENTRYDIALOG* radiusDialog;
 SREAL nuLon, nuLat;
 gchar* entry;
 SCOUNTER nuRadius;
 SFLAG paint;

 if (activeNode == NULL) return FALSE;
 if ((event -> type == GDK_BUTTON_PRESS) && (event -> button == 1) &&
     (!(event -> state & GDK_SHIFT_MASK)) && (!(event -> state & GDK_CONTROL_MASK))) {
   actGeo = SearchNearestPoint (event -> x - mWinData . originX, event -> y - mWinData . originY);
   if (actGeo != NULL)
     browser = GeoRefBrowser (actGeo, FALSE);
 }
 else if ((event -> type == GDK_BUTTON_PRESS) && (event -> button == 3) &&
          (event -> state & GDK_CONTROL_MASK) && (!(event -> state & GDK_SHIFT_MASK))) {
   actGeo = SearchNearestPoint (event -> x - mWinData . originX, event -> y - mWinData . originY);
   if (actGeo != NULL) {
     term = (TERMINAL*) actGeo -> sp -> data;
     if ((mWinData . mode == EDIT_MAP_MODE) && (term -> node == activeNode))
       browser = GeoRefBrowser (actGeo, TRUE);
     else
       browser = GeoRefBrowser (actGeo, FALSE);
   }
 }
 else if ((mWinData . mode == EDIT_MAP_MODE) && (event -> button == 3) &&
          (event -> state & GDK_SHIFT_MASK) && (!(event -> state & GDK_CONTROL_MASK)) && (g_node_first_child (activeNode) == NULL)) {
   nuLon = (SREAL) (event -> x - mWinData . originX) / mWinData . xScale;
   nuLat = (SREAL) (event -> y - mWinData . originY) / mWinData . yScale;
   nuLat = FROMINCTOLAT (nuLat);
   nuLon = FROMAZMTOLON (nuLon);
   if ((nuLon > -180.00001) && (nuLon < 180.00001) && (nuLat > -90.00001) && (nuLat < 90.00001)) {
     actGeo = AddGeoRef (nuLon, nuLat, activeNode);
     PaintNewGeoRef (actGeo);
   }
 }
 else if ((mWinData . mode == EDIT_MAP_MODE) && (event -> button == 1) &&
          (event -> state & GDK_SHIFT_MASK) && (event -> state & GDK_CONTROL_MASK) && (g_node_first_child (activeNode) == NULL)) {
   radiusDialog = CounterEntryDialog (GTK_WINDOW (mapWindow), GETGRIDSIZE, "Delete around", "Radius");
   if (gtk_dialog_run (GTK_DIALOG (radiusDialog -> dialog)) == GTK_RESPONSE_ACCEPT) {
     entry = g_strdup_printf (gtk_entry_get_text (GTK_ENTRY (radiusDialog -> entry)));
     SetCommandLine (entry);
     g_free (entry);
     if (GetCmdNumber (&nuRadius) != FALSE) {
       actNode = (PHYLONODE*) activeNode -> data;
       if (actNode -> term == NULL) return FALSE;
       term = actNode -> term;
       nuLon = (SREAL) (event -> x - mWinData . originX) / mWinData . xScale;
       nuLat = (SREAL) (event -> y - mWinData . originY) / mWinData . yScale;
       navGeo = term -> firstGeo;
       paint = FALSE;
       while (navGeo != NULL) {
         actGeo = (GEOREF*) navGeo -> data;
         navGeo = g_list_next (navGeo);
         if (term != (TERMINAL*) actGeo -> sp -> data) break;
         if ((actGeo -> inclination > (nuLat - nuRadius)) && (actGeo -> inclination < (nuLat + nuRadius)) &&
             (actGeo -> azimuth > (nuLon - nuRadius)) && (actGeo -> azimuth < (nuLon + nuRadius))) {
           paint = TRUE;
           DeleteGeoRef (actGeo);
         }
       }
       if (paint == TRUE)
         DrawMap ();
     }
   }
   DestroyEntryDialog (radiusDialog);
 }
 return FALSE;
}

void OnMenuLoadRaster (GtkMenuItem* openMenu, gpointer data) {
 GtkWidget* dialog;
 GtkFileFilter* filter;
 GdkPixbuf* nuMap;
 gchar* fileName;

 fileName = NULL;
 dialog = gtk_file_chooser_dialog_new ("Open Map (raster)", GTK_WINDOW (GETMAINWIN), GTK_FILE_CHOOSER_ACTION_OPEN,
                                       GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, NULL);
 filter = gtk_file_filter_new ();
 gtk_file_filter_add_pixbuf_formats (filter);
 gtk_file_chooser_set_filter (GTK_FILE_CHOOSER (dialog), filter);
 if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT)
   fileName = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
 gtk_widget_destroy (dialog);
 if (fileName != NULL) {
   nuMap = gdk_pixbuf_new_from_file (fileName, NULL);
   if (nuMap != NULL) {
     if (mWinData . mapImg != NULL)
       g_object_unref (mWinData . mapImg);
     mWinData . mapImg = nuMap;
     mWinData . width = gdk_pixbuf_get_width (nuMap);
     mWinData . height = gdk_pixbuf_get_height (nuMap);
     mWinData . originX = mWinData . originY = 0;
     mWinData . startX = mWinData . startY = 0;
     mWinData . north = mWinData . west = 0;
     mWinData . south = 180.0000;
     mWinData . east = 360.0000;
     PrepareGeoRefs (TRUE);
     PrepareVectorMap (TRUE, 0, 0);
     DrawMap ();
   }
 }
 g_free (fileName);
}

void OnMenuLoadVector (GtkMenuItem* openMenu, gpointer data) {
 GtkWidget* dialog;
 gchar* fileName;
 guint timer;
 PROGRESSSTRUCT progress;
 PARSINGPROGDLG* progDlg;

 fileName = NULL;
 dialog = gtk_file_chooser_dialog_new ("Open Map (vector)", GTK_WINDOW (GETMAINWIN), GTK_FILE_CHOOSER_ACTION_OPEN,
                                       GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, NULL);
 CreateFileFilter ("KML (Google Earth) file (*.kml)", "*.kml", dialog);
 CreateFileFilter ("Vector map for vNDM (*.xyd)", "*.xyd", dialog);
 CreateFileFilter ("All files (*.*)", "*.*", dialog);
 if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT)
   fileName = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
 gtk_widget_destroy (dialog);
 if (fileName != NULL) {
   progress . isActive = TRUE;
   progDlg = ParsingProgress (&progress);
   timer = g_timeout_add (100, UpdateParsingProgress, progDlg);
   if (ReadMapFile (fileName, &progress) == TRUE) {
     PrepareVectorMap (TRUE, 0, 0);
     DrawMap ();
   }
   progress . isActive = FALSE;
 }
 g_free (fileName);
}

void OnMenuSave (GtkMenuItem* saveMenu, gpointer data) {
 GtkWidget* dialog;
 GtkFileFilter* fullJpgFilter;
 GtkFileFilter* actualJpgFilter;
 GtkFileFilter* actualKMLFilter;
 GtkFileFilter* treeKMLFilter;
 GtkFileFilter* userFilter;
 GdkPixmap* tmpPix;
 GdkPixbuf* tmpBuf;
 gchar* fileName;
 SFLAG modeRecords;
 PHYLONODE* node;

 fileName = NULL;
 dialog = gtk_file_chooser_dialog_new ("Save File", GTK_WINDOW (GETMAINWIN), GTK_FILE_CHOOSER_ACTION_SAVE,
                                       GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT, NULL);
 gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (dialog), TRUE);
 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (dialog), "unitled");
 fullJpgFilter = CreateFileFilter ("Image of full map (*.jpg)", "*.jpg", dialog);
 actualJpgFilter = CreateFileFilter ("Image of actual screen (*.jpg)", "*.jpg", dialog);
 if (activeNode != NULL)
   actualKMLFilter = CreateFileFilter ("KML file of actual node reconstruction (*.kml)", "*.kml", dialog);
 else
   actualKMLFilter = NULL;
 if (GetGridState () == TRUE)
   treeKMLFilter = CreateFileFilter ("KML of vicariant nodes of actual tree reconstruction (*.kml)", "*.kml", dialog);
 else
   treeKMLFilter = NULL;
 if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT) {
   fileName = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
   userFilter = gtk_file_chooser_get_filter (GTK_FILE_CHOOSER (dialog));
   if (userFilter == fullJpgFilter) {
     fileName = CheckExtention (fileName, "jpg");
     tmpPix = gdk_pixmap_new (mWinData . drawable -> window, mWinData . width, mWinData . height, -1);
     DrawPixMap (tmpPix, mWinData . width, mWinData . height, 0, 0);
     tmpBuf = gdk_pixbuf_get_from_drawable (NULL, tmpPix, NULL, 0, 0, 0, 0, mWinData . width, mWinData . height);
     gdk_pixbuf_save (tmpBuf, fileName, "jpeg", NULL, "quality", "100", NULL);
     g_object_unref (tmpPix);
     g_object_unref (tmpBuf);
   }
   else if (userFilter == actualJpgFilter) {
     fileName = CheckExtention (fileName, "jpg");
     tmpPix = gdk_pixmap_new (mWinData . drawable -> window, mWinData . clientX, mWinData . clientY, -1);
     DrawPixMap (tmpPix, mWinData . clientX, mWinData . clientY, mWinData . originX, mWinData . originY);
     tmpBuf = gdk_pixbuf_get_from_drawable (NULL, tmpPix, NULL, 0, 0, 0, 0, mWinData . clientX, mWinData . clientY);
     gdk_pixbuf_save (tmpBuf, fileName, "jpeg", NULL, "quality", "100", NULL);
     g_object_unref (tmpPix);
     g_object_unref (tmpBuf);
   }
   else if ((userFilter == actualKMLFilter) && (actualKMLFilter != NULL)) {
     fileName = CheckExtention (fileName, "kml");
     modeRecords = (mWinData . viewMode == VIEW_RECORDS);
     WriteKMLRecons (fileName, activeNode, modeRecords, gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (mWinData . menuDrawRemoved)));
   }
   else if ((userFilter == treeKMLFilter) && (treeKMLFilter != NULL)) {
     fileName = CheckExtention (fileName, "kml");
     node = (PHYLONODE*) activeNode -> data;
     modeRecords = (mWinData . viewMode == VIEW_RECORDS);
     WriteKMLTree (fileName, node -> tree, modeRecords,
                               gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (mWinData . menuDrawRemoved)));
   }
   g_free (fileName);
 }
 gtk_widget_destroy (dialog);
}

void OnMenuLimits (GtkMenuItem* limitsMenu, gpointer data) {
 MAPLIMITSDIALOG* mapLimitsDlg;
 gchar* entry;
 SREAL tmp, nuN, nuS, nuW, nuE;

 mapLimitsDlg = MapLimitsDialog (FROMINCTOLAT (mWinData . north), FROMINCTOLAT (mWinData . south), FROMAZMTOLON (mWinData . east),
                                 FROMAZMTOLON (mWinData . west));
 nuN = mWinData . north;
 nuS = mWinData . south;
 nuE = mWinData . east;
 nuW = mWinData . west;
 if (gtk_dialog_run (GTK_DIALOG (mapLimitsDlg -> dialog)) == GTK_RESPONSE_ACCEPT) {
   entry = g_strdup_printf (gtk_entry_get_text (GTK_ENTRY (mapLimitsDlg -> northEntry)));
   SetCommandLine (entry);
   g_free (entry);
   if (GetCmdReal (&tmp) != FALSE) {
     if ((tmp > -90.0000) && (tmp < 90.0000))
       nuN = FROMLATTOINC (tmp);
     else
       nuN = mWinData . north;
   }
   entry = g_strdup_printf (gtk_entry_get_text (GTK_ENTRY (mapLimitsDlg -> southEntry)));
   SetCommandLine (entry);
   g_free (entry);
   if (GetCmdReal (&tmp) != FALSE) {
     if ((tmp > -90.0000) && (tmp < 90.0000))
       nuS = FROMLATTOINC (tmp);
     else
       nuS = mWinData . south;
   }
   if (nuS < nuN) {
     nuN = mWinData . north;
     nuS = mWinData . south;
   }
   entry = g_strdup_printf (gtk_entry_get_text (GTK_ENTRY (mapLimitsDlg -> westEntry)));
   SetCommandLine (entry);
   g_free (entry);
   if (GetCmdReal (&tmp) != FALSE) {
     if ((tmp > -180.0000) && (tmp < 180.0000))
       nuW = FROMLONTOAZM (tmp);
     else
       nuW = mWinData . west;
   }
   entry = g_strdup_printf (gtk_entry_get_text (GTK_ENTRY (mapLimitsDlg -> eastEntry)));
   SetCommandLine (entry);
   g_free (entry);
   if (GetCmdReal (&tmp) != FALSE) {
     if ((tmp > -180.0000) && (tmp < 180.0000))
       nuE = FROMLONTOAZM (tmp);
     else
       nuE = mWinData . east;
   }
   if (nuE < nuW) {
     nuW = mWinData . west;
     nuE = mWinData . east;
   }
 }
 DestroyMapLimitsDialog (mapLimitsDlg);
 if ((nuN != mWinData . north) || (nuS != mWinData . south) || (nuE != mWinData . east) || (nuW != mWinData . west)) {
  mWinData . north = nuN;
  mWinData . south = nuS;
  mWinData . east = nuE;
  mWinData . west = nuW;
  PrepareGeoRefs (TRUE);
  PrepareVectorMap (TRUE, mWinData . originX, mWinData . originY);
  mWinData . startX = mWinData . xScale * mWinData . west;
  mWinData . startY = mWinData . yScale * mWinData . north;
  DrawMap ();
 }
}

void OnMenuCentre (GtkMenuItem* centreMenu, gpointer data) {
 CENTREDIALOG* centreDialog;
 gchar* entry;
 SREAL nuX, nuY, cLat, cLon;
 SFLAG change;

 centreDialog = CentreDialog ();
 change = FALSE;
 if (gtk_dialog_run (GTK_DIALOG (centreDialog -> dialog)) == GTK_RESPONSE_ACCEPT) {
   entry = g_strdup_printf (gtk_entry_get_text (GTK_ENTRY (centreDialog -> latEntry)));
   SetCommandLine (entry);
   g_free (entry);
   if (GetCmdReal (&cLat) != FALSE) {
     if ((cLat > -90.0000) && (cLat < 90.0000)) {
       cLat = FROMLATTOINC (cLat);
       change = TRUE;
     }
   }
   entry = g_strdup_printf (gtk_entry_get_text (GTK_ENTRY (centreDialog -> lonEntry)));
   SetCommandLine (entry);
   g_free (entry);
   if ((GetCmdReal (&cLon) != FALSE) && (change == TRUE)) {
     if ((cLon > -180.0000) && (cLon < 180.0000))
       cLon = FROMLONTOAZM (cLon);
     else
       change = FALSE;
   }
   else
     change = FALSE;
 }
 DestroyCentreDialog (centreDialog);
 if (change == TRUE) {
   nuX = mWinData . xScale * cLon;
   nuY = mWinData . yScale * cLat;
   mWinData . originX = (mWinData . clientX / 2) - nuX;
   mWinData . originY = (mWinData . clientY / 2) - nuY;
   PrepareVectorMap (FALSE, mWinData . originX, mWinData . originY);
   DrawMap ();
 }
}

void OnMenuRadius (GtkMenuItem* radiusMenu, gpointer data) {
 ENTRYDIALOG* radiusDialog;
 gchar* entry;
 SCOUNTER nuRadius;
 SFLAG paint;

 radiusDialog = CounterEntryDialog (GTK_WINDOW (mapWindow), mWinData . radius, "Set drawing radius", "Radius");
 paint = FALSE;
 if (gtk_dialog_run (GTK_DIALOG (radiusDialog -> dialog)) == GTK_RESPONSE_ACCEPT) {
   entry = g_strdup_printf (gtk_entry_get_text (GTK_ENTRY (radiusDialog -> entry)));
   SetCommandLine (entry);
   g_free (entry);
   if (GetCmdNumber (&nuRadius) != FALSE) {
     if ((nuRadius >= 0) && (nuRadius != mWinData . radius)) {
       mWinData . radius = nuRadius;
       paint = TRUE;
     }
   }
 }
 DestroyEntryDialog (radiusDialog);
 if (paint == TRUE)
   DrawMap ();
}

void OnMenuViewMode (GtkMenuItem* modeMenu, gpointer data) {
 SFLAG prevMode;

 prevMode = mWinData . viewMode;
 if (gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (mWinData . menuDrawRecords)) == TRUE)
   mWinData . viewMode = VIEW_RECORDS;
 if (gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (mWinData . menuDrawCells)) == TRUE)
   mWinData . viewMode = VIEW_CELLS;
 if (prevMode != mWinData . viewMode)
   DrawMap ();
}

void OnMenuScaledRecords (GtkMenuItem* scaledMenu, gpointer data) {
 DrawMap ();
}

void OnMenuDrawCheck (GtkMenuItem* drawCheckMenu, gpointer data) {
 if (gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (mWinData . menuDrawBarrier)) == TRUE)
   gtk_widget_set_sensitive (mWinData . menuBarrier, TRUE);
 else
   gtk_widget_set_sensitive (mWinData . menuBarrier, FALSE);
 DrawMap ();
}

void OnMenuBarrierMode (GtkMenuItem* modeMenu, gpointer data) {
 SFLAG prevMode;

 prevMode = mWinData . barrierMode;
 if (gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (mWinData . menuBarrierVoronoi)) == TRUE)
   mWinData . barrierMode = DRAW_AS_VORONOI;
 else if (gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (mWinData . menuBarrierDelaunay)) == TRUE)
   mWinData . barrierMode = DRAW_AS_DELAUNAY;
 else if (gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (mWinData . menuBarrierMidTri)) == TRUE)
   mWinData . barrierMode = DRAW_AS_MIDTRI;
 if (prevMode != mWinData . barrierMode)
   DrawMap ();
}

void OnMenuBarrierOptions (GtkMenuItem* barrierMenu, gpointer data) {
 DrawMap ();
}

void PrepareGeoRefs (SFLAG updateAll) {
 GList* navGeo;
 GEOREF* ref;

 mWinData . xScale = (SREAL) mWinData . width / (mWinData . east - mWinData . west);
 mWinData . yScale = (SREAL) mWinData . height / (mWinData . south - mWinData . north);
 mWinData . maxY = 180 * mWinData . yScale;
 for (navGeo = GETLASTGEOREF; navGeo != NULL; navGeo = g_list_previous (navGeo)) {
   ref = (GEOREF*) navGeo -> data;
   if ((updateAll == FALSE) && (ref -> actX != -1)) continue;
   ref -> actX = mWinData . xScale * ref -> azimuth;
   ref -> actY = mWinData . yScale * ref -> inclination;
 }
}

void PrepareVectorMap (SFLAG updateAll, SCOUNTER originX, SCOUNTER originY) {
 GList* navLines;
 GList* navPoints;
 LINESET* line;
 MAPPOINT* point;
 SCOUNTER i;

 for (navLines = GETMAPLINES; navLines != NULL; navLines = g_list_next (navLines)) {
   line = (LINESET*) navLines -> data;
   if (line -> drawPoints == NULL)
     line -> drawPoints = g_malloc (sizeof (GdkPoint) * line -> numPoints);
   if (updateAll == TRUE) {
     for (i = 0, navPoints = line -> mapPoints; navPoints != NULL; navPoints = g_list_next (navPoints)) {
       point = (MAPPOINT*) navPoints -> data;
       point -> x = mWinData . xScale * point -> azimuth;
       point -> y = mWinData . yScale * point -> inclination;
       line -> drawPoints [i] . x = point -> x + originX;
       line -> drawPoints [i] . y = point -> y + originY;
       ++ i;
     }
   }
   else {
     for (i = 0, navPoints = line -> mapPoints; navPoints != NULL; navPoints = g_list_next (navPoints)) {
       point = (MAPPOINT*) navPoints -> data;
       line -> drawPoints [i] . x = point -> x + originX;
       line -> drawPoints [i] . y = point -> y + originY;
       ++ i;
     }
   }
 }
}

void DrawMap () {
 GdkRectangle updateRec;

 if (mWinData . pixMap == NULL)
   mWinData . pixMap = gdk_pixmap_new (mWinData . drawable -> window, mWinData . clientX, mWinData . clientY, -1);
 DrawPixMap (mWinData . pixMap, mWinData . clientX, mWinData . clientY, mWinData . originX, mWinData . originY);
 /* Invalidate the window */
 updateRec . x = 0;
 updateRec . y = 0;
 updateRec . width = mWinData . clientX;
 updateRec . height = mWinData . clientY;
 gdk_window_invalidate_rect (mWinData . drawable -> window, &updateRec, TRUE);
}

void DrawPixMap (GdkPixmap* pixmap, SCOUNTER clientX, SCOUNTER clientY, SCOUNTER originX, SCOUNTER originY) {
 GList* navGeo;
 GList* navLines;
 LINESET* set;
 GEOREF* ref;
 PHYLONODE* node;
 GNode* navAnc;
 PHYLONODE* ancNode;
 SINTEGER x, y;
 SCOUNTER r, c, sX, sY, eX, eY;
 GdkGCValues gcVals;
 SREAL tmpNum;
 GdkGC* pen;

 gdk_draw_rectangle (pixmap, mWinData . drawable -> style -> white_gc, TRUE, 0, 0, clientX, clientY);
 if (mWinData . mapImg != NULL) {
   gdk_draw_pixbuf (pixmap, mWinData . drawable -> style -> fg_gc [GTK_WIDGET_STATE (mWinData . drawable)], mWinData . mapImg,
                    0, 0, originX + mWinData . startX, originY + mWinData . startY,
                    -1, -1, GDK_RGB_DITHER_NONE, 0, 0);
 }
 /* Draw the mpa lines, if any */
 for (navLines = GETMAPLINES; navLines != NULL; navLines = g_list_next (navLines)) {
   set = (LINESET*) navLines -> data;
   gdk_draw_lines (pixmap, mWinData . blackPen, set -> drawPoints, set -> numPoints);
 }
 /* Draw the grid */
 if (gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (mWinData . menuDrawGrid)) == TRUE) {
   sX = originX;
   eX = (360 * mWinData . xScale) + originX;
   if (((sX >= 0) && (sX <= clientX)) || ((eX >= 0) && (eX <= clientX)) || ((sX <= 0) && (eX >= clientX))) {
     if (sX < 0)
       sX = 0;
     if (eX > clientX)
       eX = clientX;
     for (r = 0; ; ++ r) {
       tmpNum = GETGRIDSIZE * r;
       if (tmpNum > 180) break;
       sY = (tmpNum * mWinData . yScale) + originY;
       if (sY < 0) continue;
       if (sY > clientY) break;
       gdk_draw_line (pixmap, mWinData . greyPen, sX, sY, eX, sY);
     }
   }
   sY = mWinData . originY;
   eY = (180 * mWinData . yScale) + originY;
   if (((sY >= 0) && (sY <= clientY)) || ((eY >= 0) && (eY <= clientY)) || ((sY <= 0) && (eY >= clientY))) {
     if (sY < 0)
       sY = 0;
     if (eY > clientY)
       eY = clientY;
     for (c = 0; ; ++ c) {
       tmpNum = GETGRIDSIZE * c;
       if (tmpNum > 360) break;
       sX = (tmpNum * mWinData . xScale) + originX;
       if (sX < 0) continue;
       if (sX > clientX) break;
       gdk_draw_line (pixmap, mWinData . greyPen, sX, sY, sX, eY);
     }
   }
 }
 /* First draw all records */
 if ((gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (mWinData . menuDrawAll)) == TRUE) &&
     (mWinData . viewMode == VIEW_RECORDS)) {
   for (navGeo = GETGEOREF; navGeo != NULL; navGeo = g_list_next (navGeo)) {
     ref = (GEOREF*) navGeo -> data;
     x = ref -> actX + originX;
     if ((x < 0) || (x > clientX)) continue;
     y = ref -> actY + originY;
     if ((y < 0) || (y > clientY)) continue;
     gdk_draw_rectangle (pixmap, mWinData . blackPen, FALSE, x - mWinData . radius, y - mWinData . radius,
                 (2 * mWinData . radius) + 1, (2 * mWinData . radius) + 1);
   }
 }
 /* Then draws the active descendants of the actual node */
 if (activeNode != NULL) {
   node = (PHYLONODE*) activeNode -> data;
   if (node -> rec == NULL)
     DrawActRec (pixmap, clientX, clientY, originX, originY, activeNode, mWinData . greenPen, NULL);
   else if (node -> rec -> nodeFlag & VIC_NODE) {
     if ((node -> rec -> removSet != NULL) && (gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (mWinData . menuDrawRemoved)))
         && (node -> rec -> removSet == node -> rec -> setB))
       pen = mWinData . whitePen;
     else
       pen = mWinData . bluePen;
     DrawActRec (pixmap, clientX, clientY, originX, originY,
                 node -> rec -> setA -> node, pen, node -> rec -> setB -> rec -> cells);
     if ((node -> rec -> removSet != NULL) && (gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (mWinData . menuDrawRemoved)))
         && (node -> rec -> removSet == node -> rec -> setA))
       pen = mWinData . whitePen;
     else
       pen = mWinData . redPen;
     DrawActRec (pixmap, clientX, clientY, originX, originY,
                 node -> rec -> setB -> node, pen, node -> rec -> setA -> rec -> cells);
     if (gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (mWinData . menuDrawBarrier)) == TRUE) {
       gdk_gc_get_values (mWinData . greenPen, &gcVals);
       if (gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (mWinData . menuBarrierAncestor)) == TRUE) {
         gdk_gc_set_line_attributes (mWinData . greenPen, gcVals . line_width, GDK_LINE_ON_OFF_DASH, gcVals . cap_style,
                                     gcVals . join_style);
         for (navAnc = node -> node -> parent; navAnc != NULL; navAnc = navAnc -> parent) {
           ancNode = (PHYLONODE*) navAnc -> data;
           if (ancNode -> rec -> nodeFlag & VIC_NODE)
             DrawBarrierFromRec (pixmap, clientX, clientY, originX, originY, ancNode -> rec);
         }
       }
       if ((gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (mWinData . menuBarrierCommon)) == TRUE) &&
           (node -> alloSet != NULL)) {
         gdk_gc_set_line_attributes (mWinData . greenPen, gcVals . line_width, gcVals . line_style, gcVals . cap_style,
                                     gcVals . join_style);
         DrawBarrierFromAlloSet (pixmap, clientX, clientY, originX, originY, node);
       }
       gdk_gc_set_line_attributes (mWinData . greenPen, gcVals . line_width + 2, gcVals . line_style, gcVals . cap_style,
                                   gcVals . join_style);
       DrawBarrierFromRec (pixmap, clientX, clientY, originX, originY, node -> rec);
       gdk_gc_set_line_attributes (mWinData . greenPen, gcVals . line_width, gcVals . line_style, gcVals . cap_style,
                                   gcVals . join_style);
     }
   }
   else
     DrawActRec (pixmap, clientX, clientY, originX, originY, activeNode, mWinData . greenPen, NULL);
 }
}

void DrawActRec (GdkPixmap* pixmap, SCOUNTER clientX, SCOUNTER clientY, SCOUNTER originX, SCOUNTER originY,
                 GNode* refNode, GdkGC* pen, BITFIELD* outCells) {
 GNode* actNode;
 GList* navGeo;
 PHYLONODE* node;
 PHYLONODE* anc;
 TERMINAL* term;
 GEOREF* ref;
 SCOUNTER c, sX, sY;
 SINTEGER x, y, x1, y1;
 BITFIELD* inCells;
 SFLAG in, out;
 SREAL radius;
 SINTEGER actNest, actAge;

 node = (PHYLONODE*) refNode -> data;
 actNest = node -> nested;
 actAge = node -> minAge;
 if (mWinData . viewMode == VIEW_RECORDS) {
   if (node -> rec != NULL)
     inCells = node -> rec -> cells;
   else
     inCells = NULL;
   for (actNode = refNode; actNode != NULL;) {
     if (IsDesc (actNode, refNode) != TRUE) break;
     node = (PHYLONODE*) actNode -> data;
     if (actNode -> parent != NULL)
       anc = (PHYLONODE*) actNode -> parent -> data;
     if ((inCells != NULL) && (actNode != refNode) && ((node -> rec -> numCells == 0) ||
                           (node -> rec -> nodeFlag & REMOV_NODE) || (node == anc -> rec -> removSet))) {
       /* A removed distribution, ignores all of its descendants */
       while (actNode != NULL) {
         if (g_node_next_sibling (actNode) != NULL) {
           actNode = g_node_next_sibling (actNode);
           break;
         }
         actNode = actNode -> parent;
       }
       continue;
     }
     if (g_node_first_child (actNode) != NULL) {
     /* Not a term, goes to the first descendant */
       actNode = g_node_first_child (actNode);
       continue;
     }
     term = node -> term;
     if (gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (mWinData . menuScaledRecords)) == TRUE) {
       if ((params . mode & USE_TIME) && (actAge != NO_AGE) && (node -> minAge != NO_AGE) && (actAge != 0))
         radius = (2.0 * mWinData . radius) * (actAge - node -> minAge) / actAge;
       else
         radius = (2.0 * mWinData . radius) / (node -> nested - actNest + 1);
     }
     else
       radius = mWinData . radius;
     for (navGeo = term -> firstGeo; navGeo != NULL; navGeo = g_list_next (navGeo)) {
       ref = (GEOREF*) navGeo -> data;
       if (term != (TERMINAL*) ref -> sp -> data) break;
       x = ref -> actX + originX;
       if ((x < 0) || (x > clientX)) continue;
       y = ref -> actY + originY;
       if ((y < 0) || (y > clientY)) continue;
       if (inCells != NULL) {
         in = IsBitOn (inCells, ref -> actCell);
         if (outCells != NULL)
           out = IsBitOn (outCells, ref -> actCell);
         else
           out = FALSE;
       }
       else {
         in = TRUE;
         out = FALSE;
       }
       if (in != TRUE) {
         if (gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (mWinData . menuDrawRemoved)) == TRUE) {
           gdk_draw_rectangle (pixmap, mWinData . whitePen, TRUE, x - radius, y - radius,
                 (2 * radius) + 1, (2 * radius) + 1);
           gdk_draw_rectangle (pixmap, mWinData . blackPen, FALSE, x - radius, y - radius,
                 (2 * radius) + 1, (2 * radius) + 1);
         }
       }
       else if ((in == TRUE) && (out != TRUE)) {
         gdk_draw_rectangle (pixmap, pen, TRUE, x - radius, y - radius,
                 (2 * radius) + 1, (2 * radius) + 1);
         gdk_draw_rectangle (pixmap, mWinData . blackPen, FALSE, x - radius, y - radius,
                 (2 * radius) + 1, (2 * radius) + 1);
       }
       else {
         gdk_draw_rectangle (pixmap, mWinData . greenPen, TRUE, x - radius, y - radius,
                 (2 * radius) + 1, (2 * radius) + 1);
         gdk_draw_rectangle (pixmap, mWinData . blackPen, FALSE, x - radius, y - radius,
                 (2 * radius) + 1, (2 * radius) + 1);
       }
     }
     while (actNode != NULL) {
       if (g_node_next_sibling (actNode) != NULL) {
         actNode = g_node_next_sibling (actNode);
         break;
       }
       actNode = actNode -> parent;
     }
   }
 }
 else {
   for (c = 0; c < GETNUMCELLS; ++ c) {
     if (IsBitOn (node -> rec -> cells, c)) {
       GetColRowFromCell (c, &sY, &sX);
       x = (((GETGRIDSIZE * sX) + GETGRIDSTARTX) * mWinData . xScale) + originX;
       x1 = (((GETGRIDSIZE * (sX + 1)) + GETGRIDSTARTX) * mWinData . xScale) + originX;
       if ((x1 < 0) || (x > clientX)) continue;
       y = (((GETGRIDSIZE * sY) + GETGRIDSTARTY) * mWinData . yScale) + originY;
       y1 = (((GETGRIDSIZE * (sY + 1)) + GETGRIDSTARTY) * mWinData . yScale) + originY;
       if ((y1 < 0) || (y > clientY)) continue;
       if (outCells != NULL)
         out = IsBitOn (outCells, c);
       else
         out = FALSE;
       if (out != TRUE)
         gdk_draw_rectangle (pixmap, pen, TRUE, x, y, x1 - x, y1 - y);
       else
         gdk_draw_rectangle (pixmap, mWinData . greenPen, TRUE, x, y, x1 - x, y1 - y);
     }
   }
 }
}

GEOREF* SearchNearestPoint (gint x, gint y) {
 GList* navGeo;
 GEOREF* actGeo;
 GEOREF* toRet;
 SINTEGER difX, difY, bestDif, actDif;
 PHYLONODE* node;

 toRet = NULL;
 bestDif = (2 * mWinData . radius) + 1;
 /* First look for points of the active terminal, if any */
 if (activeNode != NULL) {
   node = (PHYLONODE*) activeNode -> data;
   if (node -> term != NULL) {
     for (navGeo = node -> term -> firstGeo; navGeo != NULL; navGeo = g_list_next (navGeo)) {
       actGeo = (GEOREF*) navGeo -> data;
       if (actGeo -> sp != node -> term -> list) break;
       difX = x - actGeo -> actX;
       difX = (difX >= 0) ? difX : - difX;
       if (difX > (mWinData . radius + 1)) continue;
       difY = y - actGeo -> actY;
       difY = (difY >= 0) ? difY : - difY;
       if (difY > (mWinData . radius + 1)) continue;
       actDif = difX + difY;
       if (actDif < bestDif) {
         bestDif = actDif;
         toRet = actGeo;
       }
       if (actDif == 0) break;
     }
   }
 }
 if (toRet != NULL) return toRet;
 /* Then, check all points */
 for (navGeo = GETGEOREF; navGeo != NULL; navGeo = g_list_next (navGeo)) {
   actGeo = (GEOREF*) navGeo -> data;
   difX = x - actGeo -> actX;
   difX = (difX >= 0) ? difX : - difX;
   if (difX > (mWinData . radius + 1)) continue;
   difY = y - actGeo -> actY;
   difY = (difY >= 0) ? difY : - difY;
   if (difY > (mWinData . radius + 1)) continue;
   actDif = difX + difY;
   if (actDif < bestDif) {
     bestDif = actDif;
     toRet = actGeo;
   }
   if (actDif == 0) break;
 }
 return toRet;
}

void DrawBarrierFromRec (GdkPixmap* pixmap, SCOUNTER clientX, SCOUNTER clientY, SCOUNTER originX, SCOUNTER originY, RECONS* rec) {
 BITFIELD* setA;
 BITFIELD* setB;

 if (rec -> triangles == NULL) {
   setA = CreateBitField ();
   setB = CreateBitField ();
   CopyBitField (setA, rec -> setA -> rec -> obs);
   CopyBitField (setB, rec -> setB -> rec -> obs);
   BitFieldRemove (setA, rec -> setB -> rec -> cells);
   BitFieldRemove (setB, rec -> setA -> rec -> cells);
   rec -> triangles = MakeVoronoi (setA, setB);
   g_slice_free1 (GETBITFIELDSIZE, setA);
   g_slice_free1 (GETBITFIELDSIZE, setB);
 }
 DrawBarrier (pixmap, clientX, clientY, originX, originY, rec -> triangles);
}

void DrawBarrierFromAlloSet (GdkPixmap* pixmap, SCOUNTER clientX, SCOUNTER clientY, SCOUNTER originX, SCOUNTER originY, PHYLONODE* node) {
 if (node -> alloSet -> triangles == NULL)
   node -> alloSet -> triangles = MakeVoronoi (node -> alloSet -> setA, node -> alloSet -> setB);
 DrawBarrier (pixmap, clientX, clientY, originX, originY, node -> alloSet -> triangles);
}

void DrawBarrier (GdkPixmap* pixmap, SCOUNTER clientX, SCOUNTER clientY, SCOUNTER originX, SCOUNTER originY, GList* triangles) {
 GList* nav;
 SCOUNTER aR, aC, bR, bC, cR, cC;
 SCOUNTER sX, sY, eX, eY, mid;
 SCOUNTER cX, cY, nX, nY;
 DELAUNAYTRIANGLE* actTri;
 DELAUNAYTRIANGLE* neiTri;

 for (nav = triangles; nav != NULL; nav = g_list_next (nav)) {
   actTri = (DELAUNAYTRIANGLE*) nav -> data;
   aR = actTri -> row [0];
   aC = actTri -> col [0];
   bR = actTri -> row [1];
   bC = actTri -> col [1];
   cR = actTri -> row [2];
   cC = actTri -> col [2];
   aC = (GETGRIDSTARTX + (GETGRIDSIZE * aC) + (GETGRIDSIZE / 2)) * mWinData . xScale;
   bC = (GETGRIDSTARTX + (GETGRIDSIZE * bC) + (GETGRIDSIZE / 2)) * mWinData . xScale;
   cC = (GETGRIDSTARTX + (GETGRIDSIZE * cC) + (GETGRIDSIZE / 2)) * mWinData . xScale;
   aR = (GETGRIDSTARTY + (GETGRIDSIZE * aR) + (GETGRIDSIZE / 2)) * mWinData . yScale;
   bR = (GETGRIDSTARTY + (GETGRIDSIZE * bR) + (GETGRIDSIZE / 2)) * mWinData . yScale;
   cR = (GETGRIDSTARTY + (GETGRIDSIZE * cR) + (GETGRIDSIZE / 2)) * mWinData . yScale;
   sX = sY = eX = eY =-1;
   if (mWinData . barrierMode == DRAW_AS_DELAUNAY) {
     /* Draws the triangles */
     sX = aC;
     sY = aR;
     eX = bC;
     eY = bR;
     CheckYLimitsLimits (&sX, &sY, &eX, &eY);
     sX += originX;
     sY += originY;
     eX += originX;
     eY += originY;
     gdk_draw_line (pixmap, mWinData . greenPen, sX, sY, eX, eY);
     sX = aC;
     sY = aR;
     eX = cC;
     eY = cR;
     CheckYLimitsLimits (&sX, &sY, &eX, &eY);
     sX += originX;
     sY += originY;
     eX += originX;
     eY += originY;
     gdk_draw_line (pixmap, mWinData . greenPen, sX, sY, eX, eY);
     sX = bC;
     sY = bR;
     eX = cC;
     eY = cR;
     CheckYLimitsLimits (&sX, &sY, &eX, &eY);
     sX += originX;
     sY += originY;
     eX += originX;
     eY += originY;
     gdk_draw_line (pixmap, mWinData . greenPen, sX, sY, eX, eY);
   }
   else if (mWinData . barrierMode == DRAW_AS_MIDTRI) {
     /* Draw middle triangles, "pseudovoronoi" */
     if ((actTri -> state [0] != actTri -> state [1]) && ((actTri -> state [0] != 0) && (actTri -> state [1] != 0))) {
       mid = (aC > bC) ? ((aC - bC) / 2) + bC : ((bC - aC) / 2) + aC;
       sX = mid;
       mid = (aR > bR) ? ((aR - bR) / 2) + bR : ((bR - aR) / 2) + aR;
       sY = mid;
     }
     if ((actTri -> state [0] != actTri -> state [2]) && ((actTri -> state [0] != 0) && (actTri -> state [2] != 0))) {
       mid = (aC > cC) ? ((aC - cC) / 2) + cC : ((cC - aC) / 2) + aC;
       eX = mid;
       mid = (aR > cR) ? ((aR - cR) / 2) + cR : ((cR - aR) / 2) + aR;
       eY = mid;
     }
     if ((actTri -> state [1] != actTri -> state [2]) && ((actTri -> state [1] != 0) && (actTri -> state [2] != 0))) {
       if (sX != -1) {
         mid = (bC > cC) ? ((bC - cC) / 2) + cC : ((cC - bC) / 2) + bC;
         eX = mid;
         mid = (bR > cR) ? ((bR - cR) / 2) + cR : ((cR - bR) / 2) + bR;
         eY = mid;
       }
       else if (sX == -1) {
         mid = (bC > cC) ? ((bC - cC) / 2) + cC : ((cC - bC) / 2) + bC;
         sX = mid;
         mid = (bR > cR) ? ((bR - cR) / 2) + cR : ((cR - bR) / 2) + bR;
         sY = mid;
       }
     }
     CheckYLimitsLimits (&sX, &sY, &eX, &eY);
     sX += originX;
     sY += originY;
     eX += originX;
     eY += originY;
     gdk_draw_line (pixmap, mWinData . greenPen, sX, sY, eX, eY);
   }
   else {
     /* Draw Voronoi lines */
     cX = ((GETGRIDSTARTX + (GETGRIDSIZE * actTri -> cX) + (GETGRIDSIZE / 2)) * mWinData . xScale);
     cY = ((GETGRIDSTARTY + (GETGRIDSIZE * actTri -> cY) + (GETGRIDSIZE / 2)) * mWinData . yScale);
     if ((actTri -> neighbor [2] != NULL) &&
         ((actTri -> state [0] != actTri -> state [1]) && ((actTri -> state [0] != 0) && (actTri -> state [1] != 0)))) {
       neiTri = (DELAUNAYTRIANGLE*) actTri -> neighbor [2];
       if (neiTri -> id > actTri -> id) {
         nX = ((GETGRIDSTARTX + (GETGRIDSIZE * neiTri -> cX) + (GETGRIDSIZE / 2)) * mWinData . xScale);
         nY = ((GETGRIDSTARTY + (GETGRIDSIZE * neiTri -> cY) + (GETGRIDSIZE / 2)) * mWinData . yScale);
         sX = cX;
         sY = cY;
         eX = nX;
         eY = nY;
         CheckYLimitsLimits (&sX, &sY, &eX, &eY);
         sX += originX;
         sY += originY;
         eX += originX;
         eY += originY;
         gdk_draw_line (pixmap, mWinData . greenPen, sX, sY, eX, eY);
       }
     }
     if ((actTri -> neighbor [1]) &&
         ((actTri -> state [0] != actTri -> state [2]) && ((actTri -> state [0] != 0) && (actTri -> state [2] != 0)))) {
       neiTri = (DELAUNAYTRIANGLE*) actTri -> neighbor [1];
       if (neiTri -> id > actTri -> id) {
         nX = ((GETGRIDSTARTX + (GETGRIDSIZE * neiTri -> cX) + (GETGRIDSIZE / 2)) * mWinData . xScale);
         nY = ((GETGRIDSTARTY + (GETGRIDSIZE * neiTri -> cY) + (GETGRIDSIZE / 2)) * mWinData . yScale);
         sX = cX;
         sY = cY;
         eX = nX;
         eY = nY;
         CheckYLimitsLimits (&sX, &sY, &eX, &eY);
         sX += originX;
         sY += originY;
         eX += originX;
         eY += originY;
         gdk_draw_line (pixmap, mWinData . greenPen, sX, sY, eX, eY);
       }
     }
     if ((actTri -> neighbor [0] != NULL) &&
         ((actTri -> state [2] != actTri -> state [1]) && ((actTri -> state [2] != 0) && (actTri -> state [1] != 0)))){
       neiTri = (DELAUNAYTRIANGLE*) actTri -> neighbor [0];
       if (neiTri -> id > actTri -> id) {
         nX = ((GETGRIDSTARTX + (GETGRIDSIZE * neiTri -> cX) + (GETGRIDSIZE / 2)) * mWinData . xScale);
         nY = ((GETGRIDSTARTY + (GETGRIDSIZE * neiTri -> cY) + (GETGRIDSIZE / 2)) * mWinData . yScale);
         sX = cX;
         sY = cY;
         eX = nX;
         eY = nY;
         CheckYLimitsLimits (&sX, &sY, &eX, &eY);
         sX += originX;
         sY += originY;
         eX += originX;
         eY += originY;
         gdk_draw_line (pixmap, mWinData . greenPen, sX, sY, eX, eY);
       }
     }
   }
 }
}

void CheckYLimitsLimits (SCOUNTER* x1, SCOUNTER* y1, SCOUNTER* x2, SCOUNTER* y2) {
 SREAL m, b;

 if ((*y1 >= 0) && (*y1 <= mWinData . maxY) && (*y2 >= 0) && (*y2 <= mWinData . maxY)) return;
 if (*x1 == *x2) {
   if (*y1 < 0)
     *y1 = 0;
   else if (*y1 > mWinData . maxY)
     *y1 = mWinData . maxY;
   if (*y2 < 0)
     *y2 = 0;
   else if (*y2 > mWinData . maxY)
     *y2 = mWinData . maxY;
   return;
 }
 m = (SREAL) (*y2 - *y1) / (*x2 - *x1);
 b = *y1 - (*x1 * m);
 if (*y1 < 0) {
   *y1 = 0;
   *x1 = - (b / m);
 }
 else if (*y1 > mWinData . maxY) {
   *y1 = mWinData . maxY;
   *x1 = (*y1 - b) / m;
 }
 if (*y2 < 0) {
   *y2 = 0;
   *x2 = - (b / m);
 }
 else if (*y2 > mWinData . maxY) {
   *y2 = mWinData . maxY;
   *x2 = (*y2 - b) / m;
 }
}
